package com.incms.web.common.interceptor;


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Map;
import java.util.Properties;

import lombok.extern.slf4j.Slf4j;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.MappedStatement.Builder;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import com.incms.web.common.util.Page;

/**
 * 项目名称：inCms    
 * 类名称：PageIntercepter    
 * 类描述： mybtatis分页拦截器
 * 创建人：liujunqing    
 * 创建时间：2015年10月15日  
 * @version 1.0    
 *
 */
@Intercepts({
	@Signature(type=Executor.class,method="query",args={ MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class })
})
@Slf4j
public class PageInterceptor implements Interceptor{

	@Override
	public Object intercept(Invocation in) throws Throwable {
		try {
			MappedStatement mappedStatement = (MappedStatement)in.getArgs()[0];
			Object paramter = in.getArgs()[1];
			BoundSql boundSql= mappedStatement.getBoundSql(paramter);
			String originalSql = boundSql.getSql().trim();
			Object parameterObject = boundSql.getParameterObject();
			//log.info("boundSql:"+boundSql.getSql()+",parameterObject:"+parameterObject);
			if(parameterObject instanceof Map){
				Map<String,Object> map = (Map) parameterObject;
				Boolean countFalg = (Boolean)map.get("countFalg");
				//log.info("countFalg:"+countFalg);
				if(countFalg != null){
					Page page = (Page)map.get("page");
					String countSql  = getCountSql(originalSql);
					Connection connection=mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection();
					PreparedStatement countStmt = connection.prepareStatement(countSql);
					BoundSql countBS = copyFromBoundSql(mappedStatement, boundSql, countSql);  
	    	        DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, countBS);  
	    	       parameterHandler.setParameters(countStmt);  
	    	       ResultSet rs = countStmt.executeQuery();  
	    	       int count=0;  
	    	       if (rs.next()) {    
	    	        count = rs.getInt(1);    
	    	       }  
	    	       rs.close();    
	    	       countStmt.close();    
	    	       connection.close(); 
	    	       page.setCount(count);
	    	       
	    	       StringBuffer sb = new StringBuffer();  
	    	       sb.append(originalSql);//.append(" LIMIT ").append(page.getFirstResult()).append(",").append(page.getPageSize());
	    	       BoundSql newBoundSql = copyFromBoundSql(mappedStatement, boundSql, sb.toString());  
		    	   MappedStatement newMs = copyFromMappedStatement(mappedStatement,new BoundSqlSqlSource(newBoundSql));  
		    	   in.getArgs()[0] = newMs;
	    	       
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			log.error(e.getMessage(),e);
			throw new RuntimeException(e.getMessage(),e);
		}
		
		return in.proceed();
	}

	private String getCountSql(String originalSql) {
		return "SELECT COUNT(*) FROM (" + originalSql + ") aliasForPage"; 
	}

	@Override
	public Object plugin(Object target) {
		 return Plugin.wrap(target, this);  
	}

	@Override
	public void setProperties(Properties arg0) {
		// TODO Auto-generated method stub
		
	}

	
	/** 
	   * 复制MappedStatement对象 
	   */  
	  private MappedStatement copyFromMappedStatement(MappedStatement ms,SqlSource newSqlSource) {  
	    Builder builder = new Builder(ms.getConfiguration(),ms.getId(),newSqlSource,ms.getSqlCommandType());  
	    builder.resource(ms.getResource());  
	    builder.fetchSize(ms.getFetchSize());  
	    builder.statementType(ms.getStatementType());  
	    builder.keyGenerator(ms.getKeyGenerator());  
	    builder.timeout(ms.getTimeout());  
	    builder.parameterMap(ms.getParameterMap());  
	    builder.resultMaps(ms.getResultMaps());  
	    builder.resultSetType(ms.getResultSetType());  
	    builder.cache(ms.getCache());  
	    builder.flushCacheRequired(ms.isFlushCacheRequired());  
	    builder.useCache(ms.isUseCache());  
	      
	    return builder.build();  
	  }  
	   
	 public class BoundSqlSqlSource implements SqlSource {    
	      BoundSql boundSql;    
	      public BoundSqlSqlSource(BoundSql boundSql) {    
	        this.boundSql = boundSql;    
	      }    
	      public BoundSql getBoundSql(Object parameterObject) {    
	        return boundSql;    
	      }    
	    }    
	 
	 /** 
	   * 复制BoundSql对象 
	   */  
	  private BoundSql copyFromBoundSql(MappedStatement ms, BoundSql boundSql, String sql) {  
	    BoundSql newBoundSql = new BoundSql(ms.getConfiguration(),sql, boundSql.getParameterMappings(), boundSql.getParameterObject());  
	    for (ParameterMapping mapping : boundSql.getParameterMappings()) {  
	        String prop = mapping.getProperty();  
	        if (boundSql.hasAdditionalParameter(prop)) {  
	            newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));  
	        }  
	    }  
	    return newBoundSql;  
	  }  
	
}
